
/*
 * Copyright (C) 2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id: XMLField.java 336 2010-02-16 17:59:45Z archie.cobbs $
 */

package org.dellroad.sidekar.xml;

import org.dellroad.sidekar.Field;
import org.dellroad.sidekar.FieldSwitchAdapter;
import org.dellroad.sidekar.Item;
import org.dellroad.sidekar.SimpleField;
import org.dellroad.sidekar.util.SidekarException;

/**
 * Container for a field during the Sidekar XML import/export process.
 */
abstract class XMLField extends FieldSwitchAdapter {

    protected String name;
    protected XMLItem xmlItem;
    protected Field<?> field;

    // Used during marshalling
    protected XMLField(String name) {
        this.name = name;
    }

    // Used during unmarshalling (fields of items)
    protected XMLField(XMLItem xmlItem) {
        this(xmlItem, null);
    }

    // Used during unmarshalling (nested fields)
    protected XMLField(XMLItem xmlItem, Field<?> field) {
        if (xmlItem == null)
            throw new IllegalArgumentException("null xmlItem");
        this.xmlItem = xmlItem;
        this.field = field;
    }

    public XMLItem getXMLItem() {
        return this.xmlItem;
    }

    /**
     * This returns the {@link Field} associated with a nested field inside this
     * field at the time of its construction.
     *
     * <p>
     * Used during unmarshalling.
     */
    protected abstract SimpleField<?> getNestedField();

    /**
     * Determine if this field has its default value.
     *
     * <p>
     * Used during marshalling.
     */
    protected abstract boolean hasDefaultValue();

    /**
     * Get the {@link Item} that contains this field.
     *
     * @throws IllegalArgumentException if this is a nested field
     */
    protected Item<?> getItem() {
        if (this.xmlItem == null || this.name == null)
            throw new IllegalArgumentException("field is nested");
        return this.xmlItem.getItem();
    }

    /**
     * Get the {@link Field} corresponding to this instance.
     */
    protected Field<?> getField() {
        if (this.field == null) {
            assert this.xmlItem != null;
            this.field = getItem().getSidekarEntity().getField(this.name);
        }
        return this.field;
    }

    @Override
    protected final <T> void caseDefault(Field<T> field) {
        String message = "wrong type for field";
        if (field.getName() != null)
            message += " \"" + field.getName() + "\"";
        throw new SidekarException(message);
    }
}

